<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Call和apply的使用</title>
</head>

<body>
    <div id="div1">这是一个div</div>
    <script>
        //apply
        var func = function (a, b, c) {
            console.log(this === window);
            console.log([a, b, c]); // 输出 [ 1, 2, 3 ]
        };
        func.apply(null, [1, 2, 3]);

        //call
        func.call(null, 1, 2, 3);

        // 1.改变this指向
        var obj1 = {
            name: 'sven'
        };
        var obj2 = {
            name: 'anne'
        };
        window.name = 'window';
        var getName = function () {
            console.log(this.name);
        };
        getName(); // 输出: window
        getName.call(obj1); // 输出: sven
        getName.call(obj2); // 输出: anne

        // 1.改变函数内部this的指向
        document.getElementById('div1').onclick = function () {
            var func = function () {
                console.log(this.id); // 输出： div1
            }
            func.call(this);
        };

        // 2.bind，扩展了函数原型，
        //简化版
        // Function.prototype.bind = function (context) {
        //     var self = this; // 保存原函数
        //     return function () { // 返回一个新的函数
        //         return self.apply(context, arguments); // 执行新的函数的时候，会把之前传入的 context
        //         // 当作新函数体内的 this
        //     }
        // };
        //完善版
        Function.prototype.bind = function () {
            var self = this, // 保存原函数
                context = [].shift.call(arguments), // 需要绑定的 this 上下文
                args = [].slice.call(arguments); // 剩余的参数转成数组
            return function () { // 返回一个新的函数
                return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
                // 执行新的函数的时候，会把之前传入的 context 当作新函数体内的 this
                // 并且组合两次分别传入的参数，作为新函数的参数
            }
        };
        var obj = {
            name: 'sven'
        };
        var func = function () {
            console.log(this.name); // 输出： sven
        }.bind(obj);
        func();

        // 3.借用其他对象的方法
        //借用构造函数
        var A = function (name) {
            this.name = name;
        };
        var B = function () {
            A.apply(this, arguments);
        };
        B.prototype.getName = function () {
            return this.name;
        };
        var b = new B('sven');
        console.log(b.getName()); // 输出： 'sven'

        (function () {
            Array.prototype.push.call(arguments, 3);
            console.log(arguments); // 输出[1,2,3]
            console.log(typeof arguments);
        })(1, 2);

    </script>
</body>

</html>